from .dl_config import *
from multiprocessing import Process, Pool
from threading import Thread
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
from concurrent import futures as ft
from concurrent.futures import Future
# 官方异步库:协程
import asyncio
# 第三方异步请求库
import aiohttp


# 多线程下载(注意线程不要开得太多)
class dl_threading(dl_base):
	
	def __init__(self, dl_urls):
		super(dl_threading, self).__init__(dl_urls)
		self.threads = []
	
	def dl_start(self):
		if len(self.dl_urls) > 0:
			try:
				for url in self.dl_urls:
					thread = Thread(target=dl_download, args=(url,))
					thread.setDaemon(True)
					thread.start()
					self.threads.append(thread)
				for t in self.threads:
					t.join()
			except Exception as e:
				logging.error('创建多线程失败: {}'.format(e))
		else:
			logging.info('未获取到请求链接')
		self.threads = []


# 异步线程池下载
class dl_threadPoolExecutor(dl_base):
	def __init__(self, dl_urls):
		super(dl_threadPoolExecutor, self).__init__(dl_urls)
	
	def dl_start(self):
		if len(self.dl_urls) > 0:
			try:
				count = check_thread_count(len(self.dl_urls))
				with ThreadPoolExecutor(max_workers=count) as tpe:
					tasks = [tpe.submit(dl_download, url) for url in self.dl_urls]
					for task in ft.as_completed(tasks):
						print(task.result())
						logging.info('下载完成:{}'.format(task.result()))
			except Exception as e:
				logging.error('创建线程池任务失败: {}'.format(e))
		else:
			logging.info('未获取到请求链接')


# 异步线程池下载方法
async def dl_async_thread_download(dl_urls):
	try:
		loop = asyncio.get_event_loop()
		tasks = []
		# 使用线程池
		count = check_thread_count(len(dl_urls))
		with ThreadPoolExecutor(max_workers=count) as tpe:
			for url in dl_urls:
				task = loop.run_in_executor(tpe, dl_download, url)
				tasks.append(task)
		done, pending = await asyncio.wait(tasks)
		for task in done:
			print(task.result())
			logging.info('下载完成: {}'.format(task.result()))
	except Exception as e:
		logging.error('创建线程池任务失败: {}'.format(e))


# 异步线程池异步下载类
class dl_threadPoolExecutor_asyncio(dl_base):
	def __init__(self, dl_urls):
		super(dl_threadPoolExecutor_asyncio, self).__init__(dl_urls)
	
	def dl_start(self):
		if len(self.dl_urls) > 0:
			loop = asyncio.get_event_loop()
			loop.run_until_complete(dl_async_thread_download(self.dl_urls))
		else:
			logging.info('未获取到请求链接')


# 多进程下载(注意进程不要开得太多,否则浪费资源耗费时间)
class dl_multiprocess(dl_base):
	def __init__(self, dl_urls):
		super(dl_multiprocess, self).__init__(dl_urls)
		self.process = []
	
	def dl_start(self):
		if len(self.dl_urls) > 0:
			try:
				for url in self.dl_urls:
					process = Process(target=dl_download, args=(url,))
					process.start()
					self.process.append(process)
				for p in self.process:
					p.join()
			except Exception as e:
				logging.error('创建多进程失败: {}'.format(e))
		else:
			logging.info('未获取到请求链接')
		self.process = []


# 进程池下载
class dl_process_pool(dl_base):
	def __init__(self, dl_urls):
		super(dl_process_pool, self).__init__(dl_urls)
	
	def dl_start(self):
		if len(self.dl_urls) > 0:
			try:
				pool = Pool(3)
				with pool:
					pool.map(dl_download, self.dl_urls)
			
			except Exception as e:
				logging.error('创建多进程失败: {}'.format(e))
		else:
			logging.info('未获取到请求链接')


# 异步进程池下载方法
async def dl_async_process_download(dl_urls):
	try:
		loop = asyncio.get_event_loop()
		tasks = []
		# 使用进程程池
		count = check_thread_count(len(dl_urls))
		with ProcessPoolExecutor(max_workers=count) as tpe:
			for url in dl_urls:
				task = loop.run_in_executor(tpe, dl_download, url)
				tasks.append(task)
		done, pending = await asyncio.wait(tasks)
		for task in done:
			print(task.result())
			logging.info('下载完成: {}'.format(task.result()))
	except Exception as e:
		logging.error('创建线程池任务失败: {}'.format(e))


# 异步进程池下载
class dl_processPoolExecutor_asyncio(dl_base):
	def __init__(self, dl_urls):
		super(dl_processPoolExecutor_asyncio, self).__init__(dl_urls)
	
	def dl_start(self):
		if len(self.dl_urls) > 0:
			loop = asyncio.get_event_loop()
			loop.run_until_complete(dl_async_process_download(self.dl_urls))
		else:
			logging.info('未获取到请求链接')


# 异步请求下载
async def dl_async_download(dl_url):
	async with aiohttp.ClientSession() as session:
		async with session.get(dl_url, verify_ssl=False) as response:
			content = await response.read()
			return content


# 混合下载方式一
class dl_many_download(dl_base):
	def __init__(self, dl_urls):
		super(dl_many_download, self).__init__(dl_urls)
	
	def dl_start(self):
		p_count, t_count = check_count(len(self.dl_urls))
		if p_count != 0:
			loop = asyncio.get_event_loop()
			loop.run_until_complete(self.dl_process_pool_download(self.dl_urls))
		else:
			loop = asyncio.get_event_loop()
			loop.run_until_complete(self.dl_threading_pool_download(self.dl_urls))
	
	async def dl_process_pool_download(self, dl_urls):
		try:
			# 使用进程程池
			count = check_thread_count(len(dl_urls))
			with ProcessPoolExecutor(max_workers=count) as tpe:
				for url in dl_urls:
					content = await dl_async_download(url)
					future = tpe.submit(write_to_file_async, content, url)
					print(future.result())
					logging.info('下载完成: {}'.format(future.result()))
		except Exception as e:
			logging.error('创建线程池任务失败: {}'.format(e))
	
	async def dl_threading_pool_download(self, dl_urls):
		try:
			# 使用进程程池
			count = check_thread_count(len(dl_urls))
			with ThreadPoolExecutor(max_workers=count) as tpe:
				for url in dl_urls:
					content = await dl_async_download(url)
					future = tpe.submit(write_to_file_async, content, url)
					print(future.result())
					logging.info('下载完成: {}'.format(future.result()))
		except Exception as e:
			logging.error('创建线程池任务失败: {}'.format(e))


# 混合下载方式二
class dl_multi_download(dl_base):
	
	def __init__(self, dl_urls):
		"""
		下载类初始化方法
		:param dl_urls: 需要下载文件链接的数组
		"""
		super(dl_multi_download, self).__init__(dl_urls)
	
	def dl_start(self):
		"""
		开始下载方法
		:return: None
		"""
		if check_thread_count(len(self.dl_urls)) < 50:
			self.dl_with_coroutine(self.dl_urls)
		else:
			p_count, t_count = check_count(self.dl_urls)
			if p_count != 0:
				self.dl_process_pool(p_count, self.dl_urls)
			else:
				self.dl_thread_pool(t_count, self.dl_urls)
	
	def dl_process_pool(self, count, p_urls):
		"""
		开启多进程下载
		:param count: 开启的进程数
		:param p_urls: 需要下载文件链接的数组
		:return: None
		"""
		try:
			p_results = dl_list_split(p_urls, count)
			with ProcessPoolExecutor(max_workers=count) as tpe:
				for sub_urls in p_results:
					future = tpe.submit(self.dl_thread_pool, 50, sub_urls)
					print(future.result())
					logging.info('下载完成: {}'.format(future.result()))
		except Exception as e:
			logging.error('创建进程池任务失败: {}'.format(e))
	
	def dl_thread_pool(self, count, t_urls):
		"""
		开启多进程下载
		:param count: 开启的进程数
		:param t_urls: 需要下载文件链接的数组
		:return: None
		"""
		try:
			t_results = dl_list_split(t_urls, count)
			with ThreadPoolExecutor(max_workers=count) as tpe:
				for sub_urls in t_results:
					future = tpe.submit(self.dl_with_coroutine, sub_urls)
					print(future.result())
					logging.info('下载完成: {}'.format(future.result()))
		except Exception as e:
			logging.error('创建线程池任务失败: {}'.format(e))
	
	def dl_with_coroutine(self, c_urls):
		"""
		使用协程异步下载
		:param c_urls: 需要下载文件链接的数组
		:return: None
		"""
		loop = asyncio.get_event_loop()
		tasks = [self.dl_file_download(url) for url in c_urls]
		loop.run_until_complete(asyncio.gather(*tasks))
	
	async def dl_file_download(self, dl_url):
		"""
		下载文件,并写入本地
		:param dl_url: 当前下载文件的链接
		:return: None
		"""
		data = await dl_async_download(dl_url)
		write_to_file_async(data, dl_url)


if __name__ == '__main__':
	urls = ['https://www.x6d.com/uploads/allimg/210602/1622617240840177.jpg',
	        'https://www.x6d.com/uploads/allimg/210602/1622617240305367.jpg',
	        'https://www.x6d.com/uploads/allimg/210602/1622617240860226.jpg']
	logging.info(json.dumps(urls))
	"""
	st = time.time()
	dl = dl_threading(urls)
	dl.dl_start()
	et = time.time()
	print(f'dl_threading 耗时: {et - st}')
	"""
	
	"""
	st = time.time()
	dl = dl_threadPoolExecutor(urls)
	dl.dl_start()
	et = time.time()
	print(f'dl_threadPoolExecutor 耗时: {et - st}')
	"""
	
	"""
	"""
	
	"""
	st = time.time()
	dl = dl_threadPoolExecutor_asyncio(urls)
	dl.dl_start()
	et = time.time()
	print(f'dl_threadPoolExecutor 耗时: {et - st}')
	"""
	
	"""
	st = time.time()
	dl = dl_multiprocess(urls)
	dl.dl_start()
	et = time.time()
	print(f'dl_multiprocess 耗时: {et - st}')

	"""
	
	"""
	st = time.time()
	dl = dl_process_pool(urls)
	dl.dl_start()
	et = time.time()
	print(f'dl_process_pool 耗时: {et - st}')
	"""
	
	"""
	st = time.time()
	dl = dl_many_download(urls)
	dl.dl_start()
	et = time.time()
	print(f'dl_many_download 耗时: {et - st}')
	"""
	
	"""
	st = time.time()
	dl = dl_multi_download(urls)
	dl.dl_start()
	et = time.time()
	print(f'dl_multi_download 耗时: {et - st}')
	"""
	
	"""
	# 分割数组测试
	nums = [x for x in range(100)]
	results = dl_list_split(nums, 3)
	for result in results:
		print(result)
	"""
